package com.fudan.cfg;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;

import com.fudan.cfg.base.ActionDef;
import com.fudan.cfg.base.DatasetDef;
import com.fudan.cfg.base.RunProperties;
import com.fudan.cfg.base.TransDef;
import com.fudan.run.action.ActionHandler;
import com.fudan.run.ctx.ActionContext;
import com.fudan.run.ctx.DatasetContext;
import com.fudan.run.ctx.EnvContext;
import com.fudan.run.ctx.TransContext;
import com.fudan.run.ctx.annotation.ActionAdapter;
import com.fudan.run.ctx.annotation.DatasetAdapter;
import com.fudan.run.ctx.annotation.TransAdapter;
import com.fudan.run.dataset.DatasetHandler;
import com.fudan.run.trans.TransHandler;

public class RunConfiguration {

	private static final String PROP_TAG = "properties";
	private static final String DATASET_TAG = "dataset";
	private static final String TRANS_TAG = "trans";
	private static final String ACTION_TAG = "action";

	private String runId;
	private RunProperties runProperties;
	private List<DatasetDef> datasetNodeList;
	private List<TransDef> transNodeList;
	private List<ActionDef> actionNodeList;

	private DatasetContext datasetContext;
	private TransContext transContext;
	private ActionContext actionContext;

	private RunConfiguration() {
		this.datasetContext = new DatasetContext();
		this.setTransContext(new TransContext());
		this.setActionContext(new ActionContext());
		loadInstance();
	}

	private void loadInstance() {
		loadEnv(DatasetAdapter.class,this.datasetContext,DatasetDef.class,DatasetHandler.class);
		loadEnv(TransAdapter.class,this.transContext,TransDef.class,TransHandler.class);
		loadEnv(ActionAdapter.class,this.actionContext,ActionDef.class,ActionHandler.class);
	}


	private void loadEnv(Class<? extends Annotation> annotation,EnvContext context,Class<?> parserClz,Class<?> handlerClz) {
		Reflections reflections = new Reflections(new ConfigurationBuilder()
				.setUrls(ClasspathHelper.forPackage("com.fudan"))
				.setScanners(new SubTypesScanner(),new TypeAnnotationsScanner()));
		Set<Class<?>> targetAdapterSet = reflections.getTypesAnnotatedWith(annotation);
		if(targetAdapterSet!=null) {
			for (Class<?> adapter : targetAdapterSet) {
				try {
					if(parserClz.isAssignableFrom(adapter)) {
						Annotation adapterAnnotation = adapter.getAnnotation(annotation);
						Method value = adapterAnnotation.getClass().getMethod("value");
						context.addParser(value.invoke(adapterAnnotation).toString(), adapter);
					}
					if(handlerClz.isAssignableFrom(adapter)) {
						Annotation adapterAnnotation = adapter.getAnnotation(annotation);
						Method value = adapterAnnotation.getClass().getMethod("value");
						context.addHandler(value.invoke(adapterAnnotation).toString(), adapter);
					}
				}catch(Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static RunConfiguration buildConfigurationFromJson(String json) {
		ObjectMapper configMapper = new ObjectMapper();
		RunConfiguration runConfiguration = new RunConfiguration();
		try {
			Map<String,Object> jsonMap = configMapper.readValue(json,new TypeReference<Map<String,Object>>() {});

			runConfiguration.extractRunProperties(jsonMap);
			runConfiguration.extractDataset(jsonMap);
			runConfiguration.extractTrans(jsonMap);
			runConfiguration.extractAction(jsonMap);

		} catch (Exception e) {
			throw new RuntimeException("配置文件解析出错",e);
		}
		return runConfiguration;
	}


	private void extractRunProperties(Map<String,Object> jsonMap) {
		this.runProperties = new RunProperties();
		this.runProperties.loadConfig((Map<String, Object>) jsonMap.get(PROP_TAG));
	}
	private void extractDataset(Map<String,Object> jsonMap) throws Exception{
		this.datasetNodeList = new ArrayList<DatasetDef>();
		List<Map<String,Object>> datasetList = (List<Map<String, Object>>) jsonMap.get(DATASET_TAG);
		if(datasetList == null) {
			throw new RuntimeException("没有定义数据集");
		}
		for (Map<String, Object> map : datasetList) {
			DatasetDef datasetNode = DatasetDef.buildFromConfiguration(map,datasetContext);
			this.datasetNodeList.add(datasetNode);
		}
	}
	private void extractTrans(Map<String,Object> jsonMap) {
		this.transNodeList = new ArrayList<>();
		List<Map<String,Object>> transList = (List<Map<String, Object>>) jsonMap.get(TRANS_TAG);
		if(transList != null) {
			for (Map<String, Object> map : transList) {
				TransDef transNode = TransDef.buildFromConfiguration(map,transContext);
				this.transNodeList.add(transNode);
			}
		}

	}
	private void extractAction(Map<String,Object> jsonMap) {
		this.actionNodeList = new ArrayList<>();
		List<Map<String,Object>> actionList = (List<Map<String, Object>>) jsonMap.get(ACTION_TAG);
		if(actionList != null) {
			for (Map<String, Object> map : actionList) {
				ActionDef actionNode = ActionDef.buildFromConfiguration(map,actionContext);
				this.actionNodeList.add(actionNode);
			}
		}
	}


	public RunProperties getRunProperties() {
		return runProperties;
	}

	public void setRunProperties(RunProperties runProperties) {
		this.runProperties = runProperties;
	}

	public List<DatasetDef> getDatasetNodeList() {
		return datasetNodeList;
	}
	public void setDatasetNodeList(List<DatasetDef> datasetNodeList) {
		this.datasetNodeList = datasetNodeList;
	}
	public List<TransDef> getTransNodeList() {
		return transNodeList;
	}
	public void setTransNodeList(List<TransDef> transNodeList) {
		this.transNodeList = transNodeList;
	}
	public List<ActionDef> getActionNodeList() {
		return actionNodeList;
	}
	public void setActionNodeList(List<ActionDef> actionNodeList) {
		this.actionNodeList = actionNodeList;
	}

	public DatasetContext getDatasetContext() {
		return datasetContext;
	}

	public void setDatasetContext(DatasetContext datasetContext) {
		this.datasetContext = datasetContext;
	}

	public TransContext getTransContext() {
		return transContext;
	}

	public void setTransContext(TransContext transContext) {
		this.transContext = transContext;
	}

	public ActionContext getActionContext() {
		return actionContext;
	}

	public void setActionContext(ActionContext actionContext) {
		this.actionContext = actionContext;
	}

	public String getRunId() {
		return runId;
	}

	public void setRunId(String runId) {
		this.runId = runId;
	}


}
